package cn.juque.redoctopus.client.appender;

import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.StrUtil;
import cn.juque.common.disruptor.DisruptorHandler;
import cn.juque.redoctopus.client.bo.LogEventBO;
import cn.juque.redoctopus.client.config.OctopusListener;
import cn.juque.redoctopus.client.disruptor.event.LogEventServiceImpl;
import cn.juque.redoctopus.client.util.IpUtil;
import cn.juque.redoctopus.client.util.OctopusContextUtil;
import com.alibaba.fastjson.JSON;
import java.io.Serializable;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.config.Property;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;

/**
 * @author nuoka
 * @version 1.0.0
 * <li>IntelliJ IDEA</li>
 * <li>cn.juque.redoctopus.api.appender</li>
 * <li>log4j2自定义日志捕捉</li>
 * @date 2020/9/9 0:10
 **/
@Plugin(name = "OctLog4j2Appender", category = "Core", elementType = "appender", printObject = true)
public class OctLog4j2Appender extends AbstractAppender {

    /**
     * 服务编号
     */
    private String serviceNo;

    /**
     * 服务名称
     */
    private String serviceName;

    /**
     * 日志级别
     */
    private Level level = Level.ERROR;

    /**
     * 输出日志的类的前缀
     */
    private String prefix;

    private OctLog4j2Appender(String serviceNo, String serviceName, Level level, String prefix,
        Filter filter, Layout<? extends Serializable> layout) {
        this(serviceName, filter, layout);
        this.serviceNo = serviceNo;
        this.serviceName = serviceName;
        this.level = level;
        this.prefix = prefix;
    }

    /**
     * @param serviceName 服务名称
     * @param filter      filter
     * @param layout      layout
     * @deprecated deprecated
     */
    public OctLog4j2Appender(String serviceName, Filter filter, Layout<? extends Serializable> layout) {
        super(serviceName, filter, layout, true, Property.EMPTY_ARRAY);
    }

    /**
     * @param serviceName      serviceName
     * @param filter           filter
     * @param layout           layout
     * @param ignoreExceptions ignoreExceptions
     * @deprecated deprecated
     */
    public OctLog4j2Appender(String serviceName,
        Filter filter, Layout<? extends Serializable> layout, boolean ignoreExceptions) {
        super(serviceName, filter, layout, ignoreExceptions, Property.EMPTY_ARRAY);
    }

    @Override
    public void append(LogEvent logEvent) {

        if (!Boolean.TRUE.equals(OctopusListener.startSuccess)) {
            return;
        }

        if (this.level.intLevel() < logEvent.getLevel().intLevel()) {
            return;
        }

        String loggerName = logEvent.getLoggerName();
        if (CharSequenceUtil.isNotEmpty(this.prefix) && !CharSequenceUtil.startWith(loggerName, this.prefix)) {
            return;
        }

        LogEventBO logEventBO = new LogEventBO();
        logEventBO.setBusinessEventService(LogEventServiceImpl.BEAN_NAME);
        logEventBO.setServiceNo(this.serviceNo);
        logEventBO.setServiceName(this.serviceName);
        logEventBO.setThreadId(logEvent.getThreadId());
        logEventBO.setIp(IpUtil.ip4Address());

        if (null != logEvent.getLevel()) {
            logEventBO.setLevel(logEvent.getLevel().toString());
        } else {
            logEventBO.setLevel("");
        }
        if (null != logEvent.getInstant()) {
            logEventBO.setTimestamp(logEvent.getInstant().getEpochMillisecond());
        } else {
            logEventBO.setTimestamp(System.currentTimeMillis());
        }
        if (null != logEvent.getMessage()) {
            logEventBO.setMessage(logEvent.getMessage().getFormattedMessage());
        } else {
            logEventBO.setMessage("message is null");
        }

        Throwable throwable = logEvent.getThrown();
        if (null != throwable) {
            logEventBO.setThrowable(JSON.toJSONString(throwable));
        }
        // 添加到队列
        DisruptorHandler disruptorHandler = OctopusContextUtil.getBean(DisruptorHandler.class);
        disruptorHandler.publish(logEventBO);
    }

    /**
     * 自定义参数
     *
     * @param serviceNo   serviceNo
     * @param serviceName serviceName
     * @param filter      filter
     * @param layout      layout
     * @param level       level
     * @param prefix      prefix
     * @return OctLog4j2Appender
     */
    @PluginFactory
    public static OctLog4j2Appender createAppender(
        @PluginAttribute("serviceNo") String serviceNo,
        @PluginAttribute("serviceName") String serviceName,
        @PluginElement("Filter") final Filter filter,
        @PluginElement("Layout") Layout<? extends Serializable> layout,
        @PluginAttribute("level") String level, @PluginAttribute("prefix") String prefix) {

        if (CharSequenceUtil.isBlank(serviceNo)) {
            serviceNo = "default";
        }
        if (CharSequenceUtil.isBlank(serviceName)) {
            serviceName = "default";
        }

        Level logLevel = Level.ERROR;
        if (CharSequenceUtil.isNotBlank(level) && null != Level.getLevel(level.toUpperCase())) {
            logLevel = Level.getLevel(level.toUpperCase());
        }

        return new OctLog4j2Appender(serviceNo, serviceName, logLevel, prefix, filter, layout);
    }
}
